/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

import java.net.Inet4Address;
import java.net.InetAddress;
import java.net.MalformedURLException;
import java.net.UnknownHostException;
import java.rmi.Naming;
import java.rmi.NotBoundException;
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 *Abstração de um host.
 * @author Cleiton
 */
public abstract class HostImpl extends UnicastRemoteObject implements Host {

    private String nome;
    private Roteador roteador;
    private String gatway;
    /**
     * Informa o endereço o Servidor ao qual os nós iram se conectar para trocarem informações
     */
   //public static String SERVIDORCENTRAL = "127.0.0.1";
   public static String SERVIDORCENTRAL = "10.28.14.45";
    private Gerenciador servidor;
    /**
     * Serve para criar id dos pacotes enviados
     */
    private int pacotesEnviados = 1;

    /**
     * Obtem o Gerenciador ao qual o host se conecta
     * @return gerenciador que o host esta conectado.
     */
    public Gerenciador getServidor() {
        return servidor;
    }

    /**
     * Altera o servidor ao qual os nos iram trocar informções.
     * @param servidor novo servidor ao qual o host sera conectado
     */
    public void setServidor(Gerenciador servidor) {
        this.servidor = servidor;
    }
    /**
     * Lista de pacotes que o host Recebeu
     */
    List<Pacote> pacotes;

    /**
     * Inicialização de um Host.
     * @throws RemoteException 
     */
    public HostImpl() throws RemoteException {
        this.pacotes = new ArrayList<Pacote>();
    }

    /**
     * Obtem o gatway ao qual o host esta conectao.
     * @return gatway ao qual o host esta conectado.
     */
    public Roteador getRoteador() {
        return roteador;
    }

    /**
     * Classe que os roteadores irão chamar para mandarem um pacote.
     * @param pacote
     * @throws RemoteException 
     */
    @Override
    public void receberMensagem(Pacote pacote) throws RemoteException {
        pacotes.add(pacote);
        atualizarTabela(pacote);
    }

    /**
     * 
     * Envia pacote para o Roteador que o Host esta conectado. Para o Roteador fazer o roteamento
     * @param pacote 
     */
    public void enviarPacote(Pacote pacote) {
        try {
            //Analisar se o roteador foi conectado ante de tentar enviar.
            roteador.receberMensagem(pacote);
        } catch (RemoteException ex) {
            Logger.getLogger(HostImpl.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    /**
     * obtem o Ipv4 da maquina
     * @return numero do ip da maquina
     * @throws RemoteException  
     */
    @Override
    public String getIp() throws RemoteException {
        InetAddress address = null;
        try {
            address = Inet4Address.getLocalHost();
        } catch (UnknownHostException ex) {
            Logger.getLogger(HostImpl.class.getName()).log(Level.SEVERE, null, ex);
        }
        return address.getHostAddress();
    }

    /**
     * 
     * @return
     */
    public String getNome() {
        return nome;
    }

    /**
     * 
     * @param nome
     */
    @Override
    public void setNome(String nome) throws RemoteException {
        this.nome = nome;
    }

    /***
     * Conectar o Host ao servidor.
     */
    public void conectarServidor() {
        try {
            servidor = (Gerenciador) Naming.lookup("rmi://" + SERVIDORCENTRAL + "/Servidor");
            System.out.println("Servidor Conectado");
        } catch (NotBoundException ex) {
            Logger.getLogger(HostImpl.class.getName()).log(Level.SEVERE, null, ex);
        } catch (MalformedURLException ex) {
            Logger.getLogger(HostImpl.class.getName()).log(Level.SEVERE, null, ex);
        } catch (RemoteException ex) {
            Logger.getLogger(HostImpl.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    /**Conecta Host ao seu Roteador gateway
     * 
     * @param name Nome do roteador ao qual o host irá se conectar.
     * @param ipRoteador Ip do roteador ao qual o host irá se conectar.
     */
    public void conectarRoteador(String name, String ipRoteador) {
        try {
            roteador = (Roteador) Naming.lookup("rmi://" + ipRoteador + "/" + name);
            gatway= name;
            System.out.println("Roteador Conectado");
        } catch (NotBoundException ex) {
            Logger.getLogger(HostImpl.class.getName()).log(Level.SEVERE, null, ex);
        } catch (MalformedURLException ex) {
            Logger.getLogger(HostImpl.class.getName()).log(Level.SEVERE, null, ex);
        } catch (RemoteException ex) {
            Logger.getLogger(HostImpl.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    /**
     * Adiciona o Host com o seu nome ao Servidor.
     */
    public void servidorCentarlAlteraNomeHost() {
        try {
            String nameHost = servidor.receber_host(this.getIp());
            this.setNome(nameHost);
        } catch (RemoteException ex) {
            Logger.getLogger(HostImpl.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
    public void enviaInformaçaoServidor(){
        try {
            servidor.atualizarCaminho(gatway+this.getNome(),gatway );
        } catch (RemoteException ex) {
            Logger.getLogger(HostImpl.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    /**
     * Cotador de Pacotes enviado pelo Host
     * @return identificação para um pacote.
     */
    public int getPacotesEnviados() {
        return pacotesEnviados++;
    }

    /**
     * Altera o Gatway do Host.Conectando o Host ao mesmo.
     * @param name Nome do Roteador ao qual o host esta conectado.
     * @param ip Ip do Roteador ao qual o Host irá se conectar.
     * @throws RemoteException
     */
    @Override
    public void setGatway(String name, String ip) throws RemoteException {
        conectarRoteador(name, ip);
        System.out.println("Meu Roteador:" + name);
    }

    /**
     * Atualiza a tabela do Host quando ele receber um determinado pacote
     * @param pacote Pacote com as informações pra quem vai enviar.
     */
    abstract public void atualizarTabela(Pacote pacote);
}
